package com.icongyou.enterprise.talent_marking.service.impl;

import com.icongyou.enterprise.common.result.Result;
import com.icongyou.enterprise.talent_marking.entity.dto.MarkRequest;
import com.icongyou.enterprise.talent_marking.entity.vo.StudentBasicInfo;
import com.icongyou.enterprise.talent_marking.entity.vo.StudentFocusMark;
import com.icongyou.enterprise.talent_marking.mapper.StudentFocusMarkMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("学生关注标记服务测试")
class StudentFocusMarkServiceImplTest {

    @Mock
    private StudentFocusMarkMapper focusMarkMapper;

    @InjectMocks
    private StudentFocusMarkServiceImpl studentFocusMarkService;

    private String userId;
    private String studentId;
    private MarkRequest markRequest;

    @BeforeEach
    void setUp() {
        userId = "USER001";
        studentId = "STUDENT001";

        markRequest = new MarkRequest();
        markRequest.setUserId(userId);
        markRequest.setStudentId(studentId);
        markRequest.setTag(1); // 重点关注
    }

    // ==================== markStudent 测试 ====================

    @Test
    @DisplayName("标记学生 - 创建新标记 - 重点关注")
    void testMarkStudent_CreateNew_ImportantFocus() {
        // Arrange
        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(null);
        doNothing().when(focusMarkMapper).insert(any(StudentFocusMark.class));

        // Act
        Result result = studentFocusMarkService.markStudent(markRequest);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("标记已创建", result.getMessage());
        assertNotNull(result.getData());

        StudentFocusMark mark = (StudentFocusMark) result.getData();
        assertEquals(userId, mark.getUserId());
        assertEquals(studentId, mark.getStudentId());
        assertEquals(1, mark.getTag());
        assertNotNull(mark.getId());
        assertNotNull(mark.getMarkedTime());

        verify(focusMarkMapper).selectByUserAndStudent(userId, studentId);
        verify(focusMarkMapper).insert(any(StudentFocusMark.class));
    }

    @Test
    @DisplayName("标记学生 - 创建新标记 - 一般关注")
    void testMarkStudent_CreateNew_NormalFocus() {
        // Arrange
        markRequest.setTag(0); // 一般关注
        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(null);
        doNothing().when(focusMarkMapper).insert(any(StudentFocusMark.class));

        // Act
        Result result = studentFocusMarkService.markStudent(markRequest);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("标记已创建", result.getMessage());

        StudentFocusMark mark = (StudentFocusMark) result.getData();
        assertEquals(0, mark.getTag());

        verify(focusMarkMapper).insert(any(StudentFocusMark.class));
    }

    @Test
    @DisplayName("标记学生 - 更新已存在的标记")
    void testMarkStudent_UpdateExisting() {
        // Arrange
        StudentFocusMark existingMark = new StudentFocusMark();
        existingMark.setId("MARK001");
        existingMark.setUserId(userId);
        existingMark.setStudentId(studentId);
        existingMark.setTag(0); // 原来是一般关注
        existingMark.setMarkedTime(new Date(System.currentTimeMillis() - 86400000)); // 昨天

        markRequest.setTag(1); // 更新为重点关注

        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(existingMark);
        doNothing().when(focusMarkMapper).updateById(any(StudentFocusMark.class));

        // Act
        Result result = studentFocusMarkService.markStudent(markRequest);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("标记已更新", result.getMessage());

        StudentFocusMark updatedMark = (StudentFocusMark) result.getData();
        assertEquals(1, updatedMark.getTag());

        verify(focusMarkMapper).selectByUserAndStudent(userId, studentId);
        verify(focusMarkMapper).updateById(existingMark);
        verify(focusMarkMapper, never()).insert(any());
    }

    @Test
    @DisplayName("标记学生 - userId为空")
    void testMarkStudent_EmptyUserId() {
        // Arrange
        markRequest.setUserId("");

        // Act & Assert
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
                () -> studentFocusMarkService.markStudent(markRequest));
        assertEquals("用户ID和学生ID不能为空", exception.getMessage());

        verify(focusMarkMapper, never()).selectByUserAndStudent(anyString(), anyString());
    }

    @Test
    @DisplayName("标记学生 - studentId为null")
    void testMarkStudent_NullStudentId() {
        // Arrange
        markRequest.setStudentId(null);

        // Act & Assert
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
                () -> studentFocusMarkService.markStudent(markRequest));
        assertEquals("用户ID和学生ID不能为空", exception.getMessage());
    }

    @Test
    @DisplayName("标记学生 - tag为null")
    void testMarkStudent_NullTag() {
        // Arrange
        markRequest.setTag(null);

        // Act & Assert
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
                () -> studentFocusMarkService.markStudent(markRequest));
        assertEquals("标签值必须是0(一般关注)或1(重点关注)", exception.getMessage());
    }

    @Test
    @DisplayName("标记学生 - tag为无效值2")
    void testMarkStudent_InvalidTag2() {
        // Arrange
        markRequest.setTag(2);

        // Act & Assert
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
                () -> studentFocusMarkService.markStudent(markRequest));
        assertEquals("标签值必须是0(一般关注)或1(重点关注)", exception.getMessage());
    }

    @Test
    @DisplayName("标记学生 - tag为无效值-1")
    void testMarkStudent_InvalidTagNegative() {
        // Arrange
        markRequest.setTag(-1);

        // Act & Assert
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
                () -> studentFocusMarkService.markStudent(markRequest));
        assertEquals("标签值必须是0(一般关注)或1(重点关注)", exception.getMessage());
    }

    // ==================== unmarkStudent 测试 ====================

    @Test
    @DisplayName("取消关注学生 - 成功")
    void testUnmarkStudent_Success() {
        // Arrange
        doNothing().when(focusMarkMapper).deleteByUserAndStudent(userId, studentId);

        // Act
        Result result = studentFocusMarkService.unmarkStudent(userId, studentId);

        // Assert
        assertEquals(200, result.getCode());
        verify(focusMarkMapper).deleteByUserAndStudent(userId, studentId);
    }

    @Test
    @DisplayName("取消关注学生 - 即使记录不存在也成功")
    void testUnmarkStudent_RecordNotExists() {
        // Arrange
        doNothing().when(focusMarkMapper).deleteByUserAndStudent(userId, studentId);

        // Act
        Result result = studentFocusMarkService.unmarkStudent(userId, studentId);

        // Assert
        assertEquals(200, result.getCode());
        verify(focusMarkMapper).deleteByUserAndStudent(userId, studentId);
    }

    // ==================== getFocusStudentsByUser 测试 ====================

    @Test
    @DisplayName("获取用户关注列表 - 有数据")
    void testGetFocusStudentsByUser_WithData() {
        // Arrange
        StudentFocusMark mark1 = new StudentFocusMark();
        mark1.setId("MARK001");
        mark1.setUserId(userId);
        mark1.setStudentId("STUDENT001");
        mark1.setTag(1);

        StudentFocusMark mark2 = new StudentFocusMark();
        mark2.setId("MARK002");
        mark2.setUserId(userId);
        mark2.setStudentId("STUDENT002");
        mark2.setTag(0);

        List<StudentFocusMark> marks = Arrays.asList(mark1, mark2);
        when(focusMarkMapper.selectByUserId(userId)).thenReturn(marks);

        // Act
        Result result = studentFocusMarkService.getFocusStudentsByUser(userId);

        // Assert
        assertEquals(200, result.getCode());
        assertNotNull(result.getData());
        @SuppressWarnings("unchecked")
        List<StudentFocusMark> resultList = (List<StudentFocusMark>) result.getData();
        assertEquals(2, resultList.size());

        verify(focusMarkMapper).selectByUserId(userId);
    }

    @Test
    @DisplayName("获取用户关注列表 - 空列表")
    void testGetFocusStudentsByUser_EmptyList() {
        // Arrange
        when(focusMarkMapper.selectByUserId(userId)).thenReturn(Collections.emptyList());

        // Act
        Result result = studentFocusMarkService.getFocusStudentsByUser(userId);

        // Assert
        assertEquals(200, result.getCode());
        assertNotNull(result.getData());
        @SuppressWarnings("unchecked")
        List<StudentFocusMark> resultList = (List<StudentFocusMark>) result.getData();
        assertTrue(resultList.isEmpty());

        verify(focusMarkMapper).selectByUserId(userId);
    }

    // ==================== isStudentFocusMarked 测试 ====================

    @Test
    @DisplayName("检查是否已关注 - 已关注")
    void testIsStudentFocusMarked_AlreadyMarked() {
        // Arrange
        StudentFocusMark existingMark = new StudentFocusMark();
        existingMark.setId("MARK001");
        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(existingMark);

        // Act
        Result result = studentFocusMarkService.isStudentFocusMarked(userId, studentId);

        // Assert
        assertEquals(200, result.getCode());
        assertTrue((Boolean) result.getData());

        verify(focusMarkMapper).selectByUserAndStudent(userId, studentId);
    }

    @Test
    @DisplayName("检查是否已关注 - 未关注")
    void testIsStudentFocusMarked_NotMarked() {
        // Arrange
        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(null);

        // Act
        Result result = studentFocusMarkService.isStudentFocusMarked(userId, studentId);

        // Assert
        assertEquals(200, result.getCode());
        assertFalse((Boolean) result.getData());

        verify(focusMarkMapper).selectByUserAndStudent(userId, studentId);
    }

    // ==================== getFocusStudentInfo 测试 ====================

    @Test
    @DisplayName("获取关注学生详细信息 - 有数据")
    void testGetFocusStudentInfo_WithData() {
        // Arrange
        StudentBasicInfo info1 = new StudentBasicInfo();
        info1.setStudentId("STUDENT001");
        info1.setRealName("张三");
        info1.setTag(1);

        StudentBasicInfo info2 = new StudentBasicInfo();
        info2.setStudentId("STUDENT002");
        info2.setRealName("李四");
        info2.setTag(0);

        List<StudentBasicInfo> infos = Arrays.asList(info1, info2);
        when(focusMarkMapper.selectFocusStudentInfoByUserId(userId)).thenReturn(infos);

        // Act
        Result result = studentFocusMarkService.getFocusStudentInfo(userId);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("获取关注学生成功", result.getMessage());
        assertNotNull(result.getData());
        @SuppressWarnings("unchecked")
        List<StudentBasicInfo> resultList = (List<StudentBasicInfo>) result.getData();
        assertEquals(2, resultList.size());

        verify(focusMarkMapper).selectFocusStudentInfoByUserId(userId);
    }

    @Test
    @DisplayName("获取关注学生详细信息 - 空列表")
    void testGetFocusStudentInfo_EmptyList() {
        // Arrange
        when(focusMarkMapper.selectFocusStudentInfoByUserId(userId)).thenReturn(Collections.emptyList());

        // Act
        Result result = studentFocusMarkService.getFocusStudentInfo(userId);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("暂无关注学生", result.getMessage());
        assertNotNull(result.getData());

        verify(focusMarkMapper).selectFocusStudentInfoByUserId(userId);
    }

    @Test
    @DisplayName("获取关注学生详细信息 - null返回")
    void testGetFocusStudentInfo_NullReturn() {
        // Arrange
        when(focusMarkMapper.selectFocusStudentInfoByUserId(userId)).thenReturn(null);

        // Act
        Result result = studentFocusMarkService.getFocusStudentInfo(userId);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("暂无关注学生", result.getMessage());

        verify(focusMarkMapper).selectFocusStudentInfoByUserId(userId);
    }

    // ==================== 边界条件测试 ====================

    @Test
    @DisplayName("标记学生 - 验证UUID生成")
    void testMarkStudent_UUIDGeneration() {
        // Arrange
        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(null);

        ArgumentCaptor<StudentFocusMark> captor = ArgumentCaptor.forClass(StudentFocusMark.class);
        doNothing().when(focusMarkMapper).insert(captor.capture());

        // Act
        studentFocusMarkService.markStudent(markRequest);

        // Assert
        StudentFocusMark captured = captor.getValue();
        assertNotNull(captured.getId());
        assertTrue(captured.getId().length() > 0);
    }

    @Test
    @DisplayName("标记学生 - 验证时间戳更新")
    void testMarkStudent_TimestampUpdate() {
        // Arrange
        StudentFocusMark existingMark = new StudentFocusMark();
        existingMark.setId("MARK001");
        existingMark.setUserId(userId);
        existingMark.setStudentId(studentId);
        existingMark.setTag(0);
        Date oldTime = new Date(System.currentTimeMillis() - 86400000);
        existingMark.setMarkedTime(oldTime);

        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(existingMark);
        doNothing().when(focusMarkMapper).updateById(any(StudentFocusMark.class));

        Date beforeUpdate = new Date();

        // Act
        studentFocusMarkService.markStudent(markRequest);

        Date afterUpdate = new Date();

        // Assert
        assertTrue(existingMark.getMarkedTime().getTime() >= beforeUpdate.getTime());
        assertTrue(existingMark.getMarkedTime().getTime() <= afterUpdate.getTime());
        assertNotEquals(oldTime, existingMark.getMarkedTime());
    }

    @Test
    @DisplayName("标记学生 - 从一般关注改为重点关注")
    void testMarkStudent_ChangeFromNormalToImportant() {
        // Arrange
        StudentFocusMark existingMark = new StudentFocusMark();
        existingMark.setId("MARK001");
        existingMark.setUserId(userId);
        existingMark.setStudentId(studentId);
        existingMark.setTag(0);

        markRequest.setTag(1);

        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(existingMark);
        doNothing().when(focusMarkMapper).updateById(any(StudentFocusMark.class));

        // Act
        Result result = studentFocusMarkService.markStudent(markRequest);

        // Assert
        assertEquals(200, result.getCode());
        assertEquals("标记已更新", result.getMessage());

        StudentFocusMark updated = (StudentFocusMark) result.getData();
        assertEquals(1, updated.getTag());
    }

    @Test
    @DisplayName("标记学生 - 从重点关注改为一般关注")
    void testMarkStudent_ChangeFromImportantToNormal() {
        // Arrange
        StudentFocusMark existingMark = new StudentFocusMark();
        existingMark.setId("MARK001");
        existingMark.setUserId(userId);
        existingMark.setStudentId(studentId);
        existingMark.setTag(1);

        markRequest.setTag(0);

        when(focusMarkMapper.selectByUserAndStudent(userId, studentId)).thenReturn(existingMark);
        doNothing().when(focusMarkMapper).updateById(any(StudentFocusMark.class));

        // Act
        Result result = studentFocusMarkService.markStudent(markRequest);

        // Assert
        assertEquals(200, result.getCode());
        StudentFocusMark updated = (StudentFocusMark) result.getData();
        assertEquals(0, updated.getTag());
    }
}
